/*
 * Copyright (c) 1994 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the Computer Systems
 *	Engineering Group at Lawrence Berkeley Laboratory.
 * 4. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

// FIXME: this is from ns2, clean it up

#ifndef _packet_h
#define _packet_h

#include <assert.h>
#include <string.h>
#include <stdlib.h>

#include "cbr_types.h"

#define FALSE 0
#define TRUE 1

// Used by wireless routing code to attach routing agent
//#define RT_PORT		255	/* port that all route msgs are sent to */

//#define IP_HDR_LEN	20
//#define IP_BROADCAST	((u_int32_t) 0xffffffff)
static const u_int32_t IP_BROADCAST = ((u_int32_t)0xffffffff);

class Packet : public Event {
private:
	unsigned char *bits_;	// header bits
//	unsigned char *data_;	// variable size buffer for 'data'
//  	unsigned int datalen_;	// length of variable size buffer
	AppData *data_;		// variable size buffer for 'data'
	static void init(Packet *);     // initialize pkt hdr
	bool fflag_;
protected:
	static Packet *free_;	// packet free list
	int	ref_count_;	// free the pkt until count to 0
public:
	Packet *next_;		// for queues and the free list
	static int hdrlen_;

	Packet() : bits_(0), data_(0), ref_count_(0), next_(0) { }
	inline unsigned char * const bits() { return (bits_); }
	inline Packet * copy() const;
	static inline Packet * alloc();
	static inline void free(Packet *);
	static inline void purge();
	inline unsigned char * access(int off) const {
		if (off < 0)
			abort();
		return (&bits_[off]);
	}
	// This is used to access application-specific data, not limited
	// to PacketData.
	inline AppData * userdata() const {
		return data_;
	}
	inline void setdata(AppData *d) {
		if (data_ != NULL)
			delete data_;
		data_ = d;
	}
	inline int datalen() const { return data_ ? data_->size() : 0; }
};

struct hdr_cmn {
	cbr_ID prev_hop_;	// addr of forwarding hop
	cbr_ID next_hop_;	// next hop for this packet

	static int offset_;	// offset for this header
	inline static hdr_cmn * access(const Packet *p) {
		return (hdr_cmn *)p->access(offset_);
	}
};

struct hdr_ip {
	static int offset_;

	inline static int& offset() { return offset_; }
	inline static hdr_ip * access(const Packet *p) {
		return (hdr_ip *)p->access(offset_);
	}
};

#define HDR_CMN(p)      (hdr_cmn::access(p))
#define HDR_IP(p)       (hdr_ip::access(p))

class PacketData : public AppData {
public:
	PacketData(int sz) : AppData() {
		datalen_ = sz;
		if (datalen_ > 0)
			data_ = new unsigned char[datalen_];
		else
			data_ = NULL;
	}
	PacketData(PacketData& d) : AppData() {
		datalen_ = d.datalen_;
 		if (datalen_ > 0) {
			data_ = new unsigned char[datalen_];
			memcpy(data_, d.data_, datalen_);
		} else
			data_ = NULL;
 	}
	virtual ~PacketData() {
		if (data_ != NULL)
			delete []data_;
 	}
 	unsigned char* data() { return data_; }

 	virtual int size() const { return datalen_; }
 	virtual AppData* copy() { return new PacketData(*this); }
private:
	unsigned char* data_;
	int datalen_;
};

inline void Packet::init(Packet *p)
{
	bzero(p->bits_, hdrlen_);
}

inline Packet* Packet::alloc()
{
	Packet *p = free_;
	if (p != 0) {
		assert(p->fflag_ == FALSE);
		free_ = p->next_;
		assert(p->data_ == 0);
		p->uid_ = 0;
		p->time_ = 0;
	} else {
		p = new Packet;
		p->bits_ = new unsigned char[hdrlen_];
		if (p == 0 || p->bits_ == 0)
			abort();
	}
	init(p); // Initialize bits_[]
	(HDR_CMN(p))->next_hop_ = -2; // -1 reserved for IP_BROADCAST
	p->fflag_ = TRUE;
	p->next_ = 0;
	return p;
}

inline void Packet::free(Packet *p)
{
	if (p->fflag_) {
		if (p->ref_count_ == 0) {
			/*
			 * A packet's uid may be < 0 (out of a event queue), or
			 * == 0 (newed but never gets into the event queue.
			 */
			assert(p->uid_ <= 0);
			// Delete user data because we won't need it any more.
			if (p->data_ != 0) {
				delete p->data_;
				p->data_ = 0;
			}
			init(p);
			p->next_ = free_;
			free_ = p;
			p->fflag_ = FALSE;
		} else {
			--p->ref_count_;
		}
	}

//	delete p;
}

inline void Packet::purge()
{
	Packet *p = free_;
	while (p != 0) {
		if (p->data_ != 0) {
			delete p->data_;
			p->data_ = 0;
		}
		free_ = p->next_;
		delete p;
		p = free_;
	}
}

inline Packet * Packet::copy() const
{

	Packet *p = alloc();
	memcpy(p->bits(), bits_, hdrlen_);
	if (data_)
		p->data_ = data_->copy();

	return p;
}

#endif
